//
//  UIViewController+CLExtension.m
//  CLExtension
//
//  Created by CharlieLiao on 2017/7/3.
//  Copyright © 2017年 Charlie. All rights reserved.
//

#import "UIViewController+CLExtension.h"
#import <objc/runtime.h>

#define SuppressPerformSelectorLeakWarning(Stuff) \
{ \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
}


static const char *assoKeyHideTimeInterval = "_k_hideTimeInterval";
static const char *assoKeyVCWaitSelDict = "_k_VCWaitSelDict";
static const char *assoKeyViewAppearStatus = "_k_ViewAppearStatus";

static BOOL _keyboardDidShow_;

@interface CLKeyboardObserver: NSObject



@end

@implementation CLKeyboardObserver

+ (void)load {
    [CLKeyboardObserver share];
}

+ (CLKeyboardObserver*)share {
    static CLKeyboardObserver *_keyBoardObserver;
    if (_keyBoardObserver == nil) {
        _keyBoardObserver = [[CLKeyboardObserver alloc] init];
    }
    return _keyBoardObserver;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cl_keyboardDidShow_:) name:UIKeyboardDidShowNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cl_keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
    }
    return self;
}

#pragma mark - Notification

- (void)cl_keyboardDidShow_:(NSNotification*)notification {
    _keyboardDidShow_ = true;
}

- (void)cl_keyboardDidHide:(NSNotification*)notification {
    _keyboardDidShow_ = false;
}

@end


@implementation UIViewController (CLExtension)

+ (void)load
{
    Method newImp;
    Method originImp;
    
    //
    newImp = class_getInstanceMethod(UIViewController.class, @selector(cl_viewDidLoad));
    originImp = class_getInstanceMethod(UIViewController.class, @selector(viewDidLoad));
    method_exchangeImplementations(originImp, newImp);
    
    //
    newImp = class_getInstanceMethod(UIViewController.class, @selector(cl_viewWillAppear:));
    originImp = class_getInstanceMethod(UIViewController.class, @selector(viewWillAppear:));
    method_exchangeImplementations(originImp, newImp);
    
    //
    newImp = class_getInstanceMethod(UIViewController.class, @selector(cl_viewDidAppear:));
    originImp = class_getInstanceMethod(UIViewController.class, @selector(viewDidAppear:));
    method_exchangeImplementations(originImp, newImp);
    
    //
    newImp = class_getInstanceMethod(UIViewController.class, @selector(cl_viewWillDisappear:));
    originImp = class_getInstanceMethod(UIViewController.class, @selector(viewWillDisappear:));
    method_exchangeImplementations(originImp, newImp);
    
    //
    newImp = class_getInstanceMethod(UIViewController.class, @selector(cl_viewDidDisappear:));
    originImp = class_getInstanceMethod(UIViewController.class, @selector(viewDidDisappear:));
    method_exchangeImplementations(originImp, newImp);
}

//- (void)dealloc {
//    objc_removeAssociatedObjects(self);
//}

#pragma mark - Life Cycle

- (void)cl_viewDidLoad {
    self.appearStatus = ViewDidLoad;
    [self cl_viewDidLoad];
}

- (void)cl_viewWillAppear:(BOOL)animated
{
    
    __weak UINavigationController *nav = self.navigationController;
    if (nav && [self respondsToSelector:@selector(hidesTopBarWhenPushed)]) {
        BOOL hidesTB = [(id<VCHideTopBarProtocol>)self hidesTopBarWhenPushed];
        BOOL isHidden = nav.isNavigationBarHidden;
        
        if (hidesTB && !isHidden) { // 需要隐藏
            [nav setNavigationBarHidden:true animated:animated];
            objc_setAssociatedObject(nav, assoKeyHideTimeInterval, @([NSDate date].timeIntervalSince1970), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
            
        } else if (!hidesTB && isHidden) { // 需要展示
            
            NSTimeInterval nowTI = [NSDate date].timeIntervalSince1970;
            
            if (@available(iOS 11.0, *)) {
                [nav setNavigationBarHidden:false animated:animated];
                
            } else { // iOS10 以下 有手势返回bug
                [nav setNavigationBarHidden:false animated:false];
                
                NSTimeInterval value = nowTI - [objc_getAssociatedObject(nav, assoKeyHideTimeInterval) doubleValue];
                
                if (value < 0.6) {
                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                        NSTimeInterval lastTI = [objc_getAssociatedObject(nav, assoKeyHideTimeInterval) doubleValue];
                        if (nowTI == lastTI && nav.isNavigationBarHidden) {
                            [nav setNavigationBarHidden:false animated:false];
                            objc_setAssociatedObject(nav, assoKeyHideTimeInterval, @([NSDate date].timeIntervalSince1970), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
                        }
                    });
                }
            }
            objc_setAssociatedObject(nav, assoKeyHideTimeInterval, @(nowTI), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
    }
    
    self.appearStatus = ViewWillAppear;
    [self cl_viewWillAppear:animated];
}


- (void)cl_viewDidAppear:(BOOL)animated
{
    self.appearStatus = ViewDidAppear;
    [self cl_viewDidAppear:animated];
}

- (void)cl_viewWillDisappear:(BOOL)animated
{
    self.appearStatus = ViewWillDisappear;
    [self cl_viewWillDisappear:animated];
}

- (void)cl_viewDidDisappear:(BOOL)animated
{
    self.appearStatus = ViewDidDisappear;
    [self cl_viewDidDisappear:animated];
}

#pragma mark - UIResponder


- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    if ([self conformsToProtocol:@protocol(VCHideKeyboardProtocol)]) {
        
        id<VCHideKeyboardProtocol> protocol = (id<VCHideKeyboardProtocol>)self;
        
        
        if (protocol.tapBgAutoHideKeyboard && _keyboardDidShow_ &&
            event != nil && event.type == UIEventTypeTouches &&
            touches.count > 0)
        {
            UITouch *touche = touches.objectEnumerator.nextObject;
            CGRect validRange = self.view.bounds;
            
            if (@available(iOS 11.0, *)) {
                UIEdgeInsets insets = self.view.safeAreaInsets;
                validRange.origin.x = insets.left;
                validRange.origin.y = insets.top;
                validRange.size.width -= insets.left+insets.right;
                validRange.size.height -= insets.top+insets.bottom;
            }
            
            CGPoint point = [touche locationInView:self.view];
            if (CGRectContainsPoint(validRange, point)) {
                if ([[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil]) {
                    [super touchesCancelled:touches withEvent:event];
                    return;
                }
            }
        }
    }
    

    [super touchesEnded:touches withEvent:event];
}

#pragma mark -

/// 当前vc 执行viewWillAppear时候，再执行selector方法
- (void)perform:(_Nonnull SEL)selector whenViewAppearStatus:(ViewAppearStatus)status
{
    if ([self respondsToSelector:selector]) {
        
        if (self.appearStatus == status) {
            SuppressPerformSelectorLeakWarning({
                [self performSelector:selector];
            });
            
        } else {
            NSString *selectorStr = NSStringFromSelector(selector);
            NSNumber *key = @(status);
            NSMutableArray *waitArr = self.waitSelDict[key];
            if (waitArr == nil) {
                waitArr = [NSMutableArray array];
                self.waitSelDict[key] = waitArr;
                [waitArr addObject:selectorStr];
                
            } else {
                if ([waitArr indexOfObject:selectorStr] != NSNotFound) {
                    [waitArr addObject:selectorStr];
                }
            }
        }
    }
}

#pragma mark - Set

///
- (NSMutableDictionary*)waitSelDict
{
    NSMutableDictionary *_waitSelDict = objc_getAssociatedObject(self, assoKeyVCWaitSelDict);
    
    if (_waitSelDict == nil) {
        _waitSelDict = [NSMutableDictionary dictionary];
        objc_setAssociatedObject(self, assoKeyVCWaitSelDict, _waitSelDict, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    return _waitSelDict;
}


- (ViewAppearStatus)appearStatus {
    return [objc_getAssociatedObject(self, assoKeyViewAppearStatus) unsignedIntegerValue];
}


- (void)setAppearStatus:(ViewAppearStatus)appearStatus
{
    if (self.appearStatus != appearStatus) {
        objc_setAssociatedObject(self, assoKeyViewAppearStatus, @(appearStatus), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
        NSMutableArray *waitArr = self.waitSelDict[@(appearStatus)];
        for (NSString *selStr in waitArr) {
            SEL selector = NSSelectorFromString(selStr);
            SuppressPerformSelectorLeakWarning({
                [self performSelector:selector];
            });
        }
        [self.waitSelDict removeObjectForKey:@(appearStatus)];
        
        if (self.waitSelDict.count == 0)
            objc_setAssociatedObject(self, assoKeyVCWaitSelDict, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
}


@end

